package com.mrdeer.user.api;

import com.mrdeer.common.domain.BusinessException;
import com.mrdeer.common.domain.PageVo;
import com.mrdeer.user.api.dto.resource.ApplicationDTO;
import com.mrdeer.user.api.dto.tenant.*;

import java.util.List;

/**
 * 租户服务：负责租户、套餐、账号等信息维护
 * 租户类型、套餐为手工维护
 *
 * @author ：xie jianchu
 * @date ：Created in 2020/9/4 0004 10:43
 */
public interface TenantService {
    /**
     * 创建租户
     * 新增租户、新增租户管理员，同时初始化套餐
     *
     * @param createTenantRequestDTO
     * @return
     */
    TenantDTO createTenantAndAccount(CreateTenantRequestDTO createTenantRequestDTO);

    /**
     * 创建用户
     * 新增租户、管理到对应管理员，同时初始化套餐
     * <p>
     * 1.若管理员用户名已存在(目前不会出现，用户名内部随机生成)
     * 2.手机号已存在，禁止创建
     * </p>
     *
     * @param createTenantRequestDTO
     * @return
     */
    TenantDTO createTenantRelateAccount(CreateTenantRequestDTO createTenantRequestDTO);

    /**
     * 删除租户
     * 解绑租户下所有账号，清除租户下的账号-角色信息，清除租户下角色，删除租户
     *
     * @param id
     * @throws BusinessException
     */
    void removeTenant(Long id) throws BusinessException;

    /**
     * 获取租户信息
     *
     * @param id
     * @return 租户信息
     * @throws BusinessException
     */
    TenantDTO getTenant(Long id) throws BusinessException;

    /**
     * 检索租户
     *
     * @param tenantQuery 租户查询条件
     * @param pageNo      查询页
     * @param pageSize    页记录数
     * @param sortBy      顺序字段
     * @param order       顺序
     * @return
     * @throws BusinessException
     */
    PageVo<TenantDTO> queryTenants(TenantQueryDTO tenantQuery, Integer pageNo, Integer pageSize, String sortBy, String order) throws BusinessException;

    /**
     * 查询某租户类型的套餐列表(不包含初始化套餐)
     *
     * @param tenantType
     * @return
     * @throws BusinessException
     */
    List<BundleDTO> queryBundleByTenantType(String tenantType) throws BusinessException;

    /**
     * 获取某套餐信息
     *
     * @param bundleCode 套餐编码
     * @return 套餐信息
     */
    BundleDTO getBundle(String bundleCode);

    /**
     * 查询所有套餐
     *
     * @return
     */
    List<BundleDTO> queryAllBundle();

    /**
     * 切换租户套餐
     * 租户切换套餐操作会删除原租户内的所有账号-角色关联数据以及原套餐产生的角色权限数据，并将限流规则写入Sentinel
     *
     * @param tenantId   租户ID
     * @param bundleCode 套餐编码
     * @throws BusinessException
     */
    void changeBundle(Long tenantId, String bundleCode) throws BusinessException;

    /**
     * 初始化租户套餐，将限流规则写入Sentinel
     *
     * @param tenantId   租户ID
     * @param bundleCode 套餐编码
     * @throws BusinessException
     */
    void initBundle(Long tenantId, String bundleCode) throws BusinessException;

    /**
     * 新增套餐
     *
     * @param bundleDTO
     * @throws BusinessException
     */
    void createBundle(BundleDTO bundleDTO) throws BusinessException;

    /**
     * 更新套餐
     *
     * @param bundleDTO
     * @throws BusinessException
     */
    void modifyBundle(BundleDTO bundleDTO) throws BusinessException;

    /**
     * 创建账号
     * 1.若用户名已存在(目前不会出现，用户名内部随机生成)，禁止创建
     * 2.手机号已存在，禁止创建
     *
     * @param createAccountRequestDTO
     * @return
     * @throws BusinessException
     */
    AccountDTO createAccount(CreateAccountRequestDTO createAccountRequestDTO) throws BusinessException;

    /**
     * 修改账号密码
     *
     * @param accountRequest
     * @return
     */
    boolean accountPassword(ChangeAccountPwdDTO accountRequest);

    /**
     * 创建账号并绑定到某租户
     * 1.若用户名已存在（目前不会出现，用户名内部随机生成），禁止创建
     * 2.手机号已存在，禁止创建
     *
     * @param createAccountRequestDTO
     * @param tenantId
     * @throws BusinessException
     */
    void createAccountInTenant(CreateAccountRequestDTO createAccountRequestDTO, Long tenantId) throws BusinessException;

    /**
     * 将已存在账号，加入到某租户(绑定关系，身份不是管理员)
     * 界面上先通过手机号查询到账号信息，在通过账号信息的username，调用此接口，加入租户
     *
     * @param tenantId 租户id
     * @param username 用户名
     * @throws BusinessException
     */
    void bindTenant(Long tenantId, String username) throws BusinessException;

    /**
     * 将某账号从租户内一处，租户管理员不可一处
     *
     * @param tenantId 租户ID
     * @param username 用户名
     * @throws BusinessException
     */
    void unbindTenant(Long tenantId, String username) throws BusinessException;

    /**
     * 根据用户名判断账号是否存在
     *
     * @param username 用户名
     * @return
     * @throws BusinessException
     */
    boolean isExistAccountByUsername(String username) throws BusinessException;

    /**
     * 根据手机号判断账号是否存在
     *
     * @param mobile
     * @return
     * @throws BusinessException
     */
    boolean isExistAccountByMobile(String mobile) throws BusinessException;

    /**
     * 根据用户名获取账号信息
     *
     * @param username 用户名
     * @return
     * @throws BusinessException
     */
    AccountDTO getAccountByUsername(String username) throws BusinessException;

    /**
     * 根据手机号获取账号信息
     *
     * @param mobile
     * @return
     * @throws BusinessException
     */
    AccountDTO getAccountByMobile(String mobile) throws BusinessException;

    /**
     * 根据用户名判断账号是否在某租户内
     *
     * @param tenantId 租户ID
     * @param username 用户名
     * @return
     * @throws BusinessException
     */
    boolean isExistAccountInTenantByUsername(Long tenantId, String username) throws BusinessException;

    /**
     * 检索账号
     *
     * @param accountQuery 账号查询条件
     * @param pageNo       查询页
     * @param pageSize     页记录数
     * @param sortBy       排序字段
     * @param order        顺序
     * @return
     * @throws BusinessException
     */
    PageVo<AccountDTO> queryAccount(AccountQueryDTO accountQuery, Integer pageNo, Integer pageSize, String sortBy, String order) throws BusinessException;

    /**
     * 查询某账号所属租户列表
     *
     * @param username 用户名
     * @return
     * @throws BusinessException
     */
    List<TenantDTO> queryAccountInTenant(String username) throws BusinessException;

    /**
     * 发送短信验证码
     *
     * @param phone
     * @return
     */
    String sendMessage(String phone);

    /**
     * 用户认证
     *
     * @param authenticationInfo 认证请求信息
     * @return 认证成功的账号信息
     * @throws BusinessException
     */
    AccountDTO authentication(AuthenticationInfo authenticationInfo) throws BusinessException;

    /**
     * 用户登录，被uaa服务调用并生成令牌
     * 1.调用authentication(AuthenticationInfo authenticationInfo) 认证接口通过认证
     * 2.调用queryAccountInTenant(String username)获取该用户所属租户列表
     * 3.授权，调用AuthorizationService.authorize(String username,Long[] tenantIds)获取该用户在多个租户下的权限
     * 4.获取资源，调用loadResources(Map<Long,AuthorizationInfoDTO> tenantAuthorizationInfoMap);获取该用户在多个租户下的资源
     *
     * @param loginRequest 登录请求
     * @return 登录响应，包括用户的租户，角色，权限资源等信息
     * @throws BusinessException 备忘录:
     *                           1.由于token设计的可以跨多个租户，当前登入租户，需由应用方(可默认选第一个租户)掌握并传递，设计有状态接口的参数中，不然无法知道用户当前的登入租户
     *                           2.在uaa生成token之前，需要根据client_id获取当前接入应用所属租户ID(ResourceService.queryApplication(String applicationCode)),并放入token,此tenantId作为限流的limitApp,也是套餐拥有者标识，用于套餐限制
     *                           3.token不能跨不同租户开发的应用
     */
    LoginInfoDTO login(LoginRequestDTO loginRequest) throws BusinessException;


    /**
     * 根据接入客户顿获取应用
     *
     * @param clientId 客户端ID
     * @return
     * @throws BusinessException
     */
    ApplicationDTO getApplicationDTOByClientId(String clientId) throws BusinessException;

    /**
     * 根据账号和身份获取租户
     *
     * @param accountId 账号ID
     * @param isAdmin   是否是管理员
     * @return
     * @throws BusinessException
     */
    TenantDTO getTenantByAccount(Long accountId, Boolean isAdmin) throws BusinessException;

    /**
     * 查询某个管理员
     *
     * @param username
     * @param roleCode
     * @param tenantId
     * @return
     */
    AccountRoleQueryDTO queryAccountRole(String username, String roleCode, Long tenantId);

    /**
     * 分页条件查询管理员
     *
     * @param accountRoleDTO
     * @param pageNo
     * @param pageSize
     * @return
     */
    PageVo<AccountRoleQueryDTO> queryAdministratorByPage(AccountRoleDTO accountRoleDTO, Integer pageNo, Integer pageSize);

    /**
     * 在租户内创建账号并绑定角色，包含校验账号是否存在及角色是否绑定
     *
     * @param tenantId
     * @param accountRequestDTO
     * @param roleCodes
     */
    void checkCreateStaffAccountRole(Long tenantId, CreateAccountRequestDTO accountRequestDTO, String[] roleCodes);

    /**
     * 更新账号的角色信息
     *
     * @param username
     * @param tenantId
     * @param roleCodes
     */
    void getAccountRoleBind(String username, Long tenantId, String[] roleCodes);
}
